{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Die grundlegenden Werkzeuge des privatsphärenerhaltenden Deep Learning \n",
    "\n",
    "Willkommen zum PySyfts Einführungs-Tutorial zum privatsphärenerhaltenden und dezentralisierten Deep Learning. Diese Reihe von Notebooks ist eine schrittweise Anleitung, mit der Sie die neuen Werkzeuge und Techniken kennenlernen können, die für das Deep Learning an geheimen oder privaten Daten und Modellen erforderlich sind, ohne sie dem Eigentum einer Instanz zu unterwerfen.\n",
    "\n",
    " \n",
    "\n",
    "**Was wir behandeln werden**: Beachten Sie, dass wir nicht nur über das Dezentralisieren / Verschlüsseln von Daten sprechen, sondern auch darüber, wie PySyft verwendet werden kann, um das gesamte Ökosystem um Daten herum zu dezentralisieren, einschließlich der Datenbanken, in denen sich Daten befinden, gespeichert oder abgefragt werden, sowie die neuronalen Netzwerke, mit denen Informationen aus Daten extrahiert werden. Wenn neue Erweiterungen für PySyft erstellt werden, werden diese Notebooks um neue Tutorials erweitert, um die neuen Funktionen zu erläutern. \n",
    "\n",
    "Autoren:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Übersetzer:\n",
    "- Vineet Jain - Github: [@vineetjai](https://github.com/vineetjai)\n",
    "- Georgios Kaissis - Twitter: [@gkaissis](https://twitter.com/gkaissis)\n",
    "\n",
    "## Gliederung:\n",
    "\n",
    "- Teil 1: Die grundlegenden Werkzeuge des privaten Deep Learning \n",
    "\n",
    "\n",
    "## Warum dieses Tutorial machen?\n",
    "\n",
    "1) Wettbewerbsvorteil für Ihre Karierre - In den letzten 20 Jahren hat die digitale Revolution die Daten in immer größeren Mengen zugänglicher gemacht, da analoge Prozesse digitalisiert wurden. Mit neuen Regelungen wie z.B. der [Datenschutzgrundverordnung](https://eugdpr.org/), stehen Unternehmen zunehmend unter Einschränkungen Ihrer Freiheit bei der Verwendung - und vor allem bei der Analyse - persönlicher Informationen. Fazit: Datenwissenschafftler werden mit \"Old School\" -Werkzeugen nicht mehr auf so viele Daten zugreifen können, aber durch das Erlernen der Tools des privatspährenerhaltenden Deep Learning können SIE dieser Kurve voraus sein und einen Wettbewerbsvorteil für Ihre Karriere erzielen. \n",
    "\n",
    "2) Unternehmerische Möglichkeiten - Es gibt eine ganze Reihe von Problemen in der Gesellschaft, die Deep Learning lösen kann, aber viele der wichtigsten wurden noch nicht untersucht, da dies den Zugriff auf sensible Informationen über Menschen erfordern würde (erwägen Sie die Verwendung von Deep Learning, um Menschen mit mentalen oder Beziehungsproblemen zu helfen!). Das Erlernen von privatspährenerhaltendem Deep Learning eröffnet Ihnen somit eine Vielzahl neuer Startmöglichkeiten, die anderen ohne diese Tools bisher nicht zur Verfügung standen. \n",
    "\n",
    "3) Sozialer Fortschritt - Deep Learning kann verwendet werden, um eine Vielzahl von Problemen in der realen Welt zu lösen, aber Deep Learning an persönlichen Informationen ist Deep Learning über Menschen, für Menschen. Das Erlernen des Deep Learning für Daten, die Sie nicht besitzen, ist mehr als eine berufliche oder unternehmerische Gelegenheit. Es ist die Gelegenheit, einige der persönlichsten und wichtigsten Probleme im Leben der Menschen zu lösen - und dies in großem Maßstab. \n",
    "\n",
    "\n",
    "\n",
    "\n",
    "## Wie bekomme ich Bonuspunkte? \n",
    "\n",
    "- Gib PySyft einen Stern auf GitHub! - https://github.com/OpenMined/PySyft\n",
    "- Mach ein Youtube-Video, in dem dieses Notebook's unterrichtet wird! \n",
    "\n",
    "\n",
    "... Ok, los geht's!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Teil -1: Voraussetzungen \n",
    "\n",
    "- Lernen Sie PyTorch - wenn nicht, nehmen Sie am http://fast.ai Kurs teil und kommen Sie zurück \n",
    "- Lesen Sie das PySyft Framework Paper https://arxiv.org/pdf/1811.04017.pdf! Auf diese Weise erhalten Sie einen umfassenden Hintergrund zum Aufbau von PySyft, wodurch die Dinge sinnvoller werden.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# Teil 0: Setup\n",
    "\n",
    "Zu Beginn müssen Sie sicherstellen, dass Sie die richtigen Dinge installiert haben. Gehen Sie dazu zum PySyft Readme und folgen Sie den Anweisungen zum Einrichten. Der einfachste Weg für die meisten ist: \n",
    "\n",
    "- Installieren Sie Python 3.6 oder höher \n",
    "- Installieren Sie PyTorch 1.4 \n",
    "- pip install syft[udacity] \n",
    "\n",
    "Wenn ein Teil davon für Sie nicht funktioniert (oder einer der Tests fehlschlägt), überprüfen Sie zuerst die [README](https://github.com/OpenMined/PySyft.git) -Installationshilfe und öffnen Sie dann ein GitHub-Problem oder schreiben Sie den #beginner Channel in unserem Slack! [slack.openmined.org](http://slack.openmined.org/)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1, 2, 3, 4, 5])"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Run this cell to see if things work\n",
    "import sys\n",
    "\n",
    "import torch\n",
    "from torch.nn import Parameter\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "torch.tensor([1,2,3,4,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Wenn diese Zelle ausgeführt wird, kann es losgehen!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Teil 1: Die grundlegenden Werkzeuge der privaten, dezentralen Datenwissenschaft \n",
    "\n",
    "Die erste Frage, die Sie sich vielleicht stellen, lautet: Wie trainieren wir ein Modell an Daten, auf die wir keinen Zugriff haben? \n",
    "\n",
    "Die Antwort ist überraschend einfach. Wenn Sie es gewohnt sind, in PyTorch zu arbeiten, sind Sie es gewohnt, mit `torch.Tensor` Objekten zu arbeiten. Tensor-Objekte wie diese!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([ 2,  4,  6,  8, 10])\n"
     ]
    }
   ],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = x + x\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Natürlich ist die Verwendung dieser Tensoren wichtig, erfordert aber auch, dass Sie die Daten auf Ihrem lokalen Computer haben. Hier beginnt unsere Reise. \n",
    "\n",
    "# Abschnitt 1.1 - Senden von Tensoren an Bobs Maschine \n",
    "\n",
    "Während wir normalerweise Data Science / Deep Learning auf der Maschine durchführen würden, die die Daten hat, möchten wir diese Art der Berechnung jetzt auf einer anderen Maschine durchführen. Insbesondere können wir also nicht mehr einfach davon ausgehen, dass sich die Daten auf unserem lokalen Computer befinden. \n",
    "\n",
    "Anstatt PyTorch Tensoren zu verwenden, werden wir jetzt mit pointers zu Tensoren arbeiten. Lassen Sie uns zunächst eine \"virtuelle\" Maschine erstellen, die einer \"virtuellen\" Person gehört - wir nennen sie Bob.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id=\"bob\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nehmen wir an, Bobs Maschine befindet sich auf einem anderen Planeten - vielleicht auf dem Mars! Aber im Moment ist die Maschine leer. Lassen Sie uns einige Daten erstellen, damit wir sie an Bob senden und mehr über pointer erfahren können!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5])\n",
    "y = torch.tensor([1,1,1,1,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Und jetzt - lass uns unsere Tensoren an Bob schicken !!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_ptr = x.send(bob)\n",
    "y_ptr = y.send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:83083126699 -> bob:57691089902]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BOOM! Jetzt hat Bob zwei Tensoren! Glauben Sie mir nicht? Überzeugen Sie sich selbst!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{57691089902: tensor([1, 2, 3, 4, 5]), 78260268274: tensor([1, 1, 1, 1, 1])}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = x_ptr + x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:39740619599 -> bob:55000474416]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{57691089902: tensor([1, 2, 3, 4, 5]),\n",
       " 78260268274: tensor([1, 1, 1, 1, 1]),\n",
       " 55000474416: tensor([ 2,  4,  6,  8, 10])}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Jetzt bemerken wir: Als wir `x.send(bob)` aufriefen, gab es ein neues Objekt zurück, das wir `x_ptr` nannten. Dies ist unser erster pointer auf einen Tensor. Pointer auf Tensoren enthalten KEINE Daten. Stattdessen enthalten sie einfach Metadaten zu einem Tensor (der Daten enthält), die auf einem anderen Computer gespeichert sind. Der Zweck dieser Tensoren besteht darin, uns eine intuitive Benutzerumgebung zu geben, mit der die andere Maschine angewiesen wird, Funktionen mit diesem Tensor zu berechnen. Werfen wir einen Blick auf die Metadaten, die die pointer enthalten."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:83083126699 -> bob:57691089902]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Schauen wir die Metadaten an! \n",
    "\n",
    "Es gibt zwei Hauptattribute, die für pointer spezifisch sind: \n",
    "\n",
    "- `x_ptr.location : bob`, der Ort, also ein Verweis auf den Ort, auf den der Zeiger zeigt \n",
    "- `x_ptr.id_at_location : <random integer>`, die ID des Tensors an diesem Ort\n",
    "Sie werden im Format \"@\" gedruckt \n",
    "\n",
    "Es gibt auch andere allgemeinere Merkmale: \n",
    "\n",
    "- `x_ptr.id : <random integer>`, die ID unseres pointer Tensors, zufällig zugewiesen \n",
    "- `x_ptr.owner : \"me\"`, der Arbeiter, dem der Zeigertensor gehört, in diesem Fall ist es der lokale Arbeiter mit dem Namen \"ich\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:bob #objects:3>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:bob #objects:3>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob==x_ptr.location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "57691089902"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.id_at_location"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:me #objects:0>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sie fragen sich vielleicht, warum der lokale Arbeiter (Worker), dem der Zeiger gehört, auch ein VirtualWorker ist, obwohl wir ihn nicht erstellt haben.  Genau wie es ein VirtualWorker-Objekt für Bob gibt, gibt es (standardmäßig) immer auch eines für uns selbst. Dieser Worker wird automatisch erstellt, wenn wir `hook = sy.TorchHook()` aufrufen. Daher müssen Sie ihn normalerweise nicht selbst erstellen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<VirtualWorker id:me #objects:0>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "me = sy.local_worker\n",
    "me"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "me == x_ptr.owner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Und schließlich können wir, genau wie wir `.send()` für einen Tensor aufrufen können, `.get()` für einen pointer auf einen Tensor aufrufen, um ihn zurückzubekommen !!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:83083126699 -> bob:57691089902]"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1, 2, 3, 4, 5])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:17829909481 -> bob:78260268274]"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1, 1, 1, 1, 1])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_ptr.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 2,  4,  6,  8, 10])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Und wie Sie sehen können ... hat Bob die Tensoren nicht mehr !!! Sie sind zurück auf unsere Maschine umgezogen!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Abschnitt 1.2 - Verwenden von Tensor-pointern \n",
    "\n",
    "Das Senden und Empfangen von Tensoren von Bob ist zwar großartig, aber das ist kaum Deep Learning! Wir möchten in der Lage sein, Tensorberechnungen an entlegenen Tensoren durchzuführen. Glücklicherweise machen Tensor-pointer dies ziemlich einfach! Sie können einfach diese pointer wie normale Tensoren verwenden!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5]).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "z=x+y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:12028202952 -> bob:76071755840]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Et voilà! \n",
    "\n",
    "Hinter den Kulissen ist etwas interessantes passiert. Anstatt zwischen x und y eine lokale Addition zu berechnen, wurde der Befehl serialisiert und an Bob gesendet, der die Berechnung durchführte, einen Tensor z erstellte und dann den pointer auf z an uns zurückschickte! \n",
    "\n",
    "Wenn wir `.get()` für den Zeiger aufrufen, erhalten wir das Ergebnis zurück an unsere Maschine!\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([2, 3, 4, 5, 6])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Torch-Funktionen \n",
    "\n",
    "Diese API wurde auf alle Operationen von torch erweitert !!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:97521977565 -> bob:75592019932]"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:92075559708 -> bob:7890228100]"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:71694644898 -> bob:48970175826]"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = torch.add(x,y)\n",
    "z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([2, 3, 4, 5, 6])"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variablen (einschließlich Backpropagation!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([1,2,3,4,5.], requires_grad=True).send(bob)\n",
    "y = torch.tensor([1,1,1,1,1.], requires_grad=True).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "z = (x + y).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(Wrapper)>[PointerTensor | me:22226407463 -> bob:57302226904]"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = x.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1., 2., 3., 4., 5.], requires_grad=True)"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1., 1., 1., 1., 1.])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Wie Sie sehen können, ist die API sehr flexibel und kann nahezu alle Vorgänge ausführen, die Sie normalerweise in Torch für Remote-Daten ausführen würden. Dies bildet die Grundlage für unsere erweiterten Protokolle zur Wahrung der Privatsphäre wie Federated Learning, Secure Multi-Party Computation und Differential Privacy!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Herzlichen Glückwunsch!!! - Zeit, der Community beizutreten! \n",
    "\n",
    "Herzlichen Glückwunsch zum Abschluss dieses Notebook-Tutorials! Wenn es Ihnen gefallen hat und Sie sich der Bewegung zur Wahrung der Privatsphäre, zum dezentralisiertenen Besitz von KI und der KI-Lieferkette (Daten) anschließen möchten, können Sie dies auf folgende Weise tun! \n",
    "\n",
    "### PySyft auf GitHub einen Stern geben! \n",
    "\n",
    "Der einfachste Weg, unserer Community zu helfen, besteht darin, die GitHub-Repos mit Sternen auszuzeichnen! Dies hilft, das Bewusstsein für die coolen Tools zu schärfen, die wir bauen. \n",
    "\n",
    "- [Gib PySyft einen Stern](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Mach mit bei Slack! \n",
    "\n",
    "Der beste Weg, um über die neuesten Entwicklungen auf dem Laufenden zu bleiben, ist, sich unserer Community anzuschließen! Sie können dies tun, indem Sie das Formular unter [http://slack.openmined.org](http://slack.openmined.org) ausfüllen.\n",
    "\n",
    "### Treten Sie einem Code-Projekt bei! \n",
    "\n",
    "Der beste Weg, um zu unserer Community beizutragen, besteht darin, Entwickler zu werden! Sie können jederzeit zur PySyft GitHub Issues-Seite gehen und nach \"Projects\" filtern. Dies zeigt Ihnen alle Top-Level-Tickets und gibt einen Überblick darüber, an welchen Projekten Sie teilnehmen können! Wenn Sie nicht an einem Projekt teilnehmen möchten, aber ein wenig programmieren möchten, können Sie auch nach weiteren \"einmaligen\" Miniprojekten suchen, indem Sie nach GitHub-Problemen suchen, die als \"good first issue\" gekennzeichnet sind. \n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Spenden\n",
    "\n",
    "Wenn Sie keine Zeit haben, zu unserer Codebase beizutragen, aber dennoch Unterstützung leisten möchten, können Sie auch Unterstützer unseres Open Collective werden. Alle Spenden fließen in unser Webhosting und andere Community-Ausgaben wie Hackathons und Meetups! \n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
